import Seed from './seed';
import { filename } from './util';
import { defaults, isFunction } from 'lodash';
import { isWebRTCSupported } from 'detectrtc';
import { logInfo, logError, setDebug } from './debugger';
export default class DataBox {
    constructor(
        host,
        id,
        {
            timeout = 1500,
            forceCDN = false,
            cacheSize = 419430400,
            debug = false,
            trytimes = 5,
            ioOpts = {},
            onreport = () => {}
        } = {}
    ) {
        this.id = id;
        this.seed = null;
        this.trytimes = trytimes;

        //options
        this.timeout = timeout;
        this.forceCDN = forceCDN;
        this.onreport = onreport;

        setDebug(debug);

        if (isWebRTCSupported || !forceCDN)
            this.seed = new Seed(host, id, { cacheSize, ioOpts });
    }

    static CDN = 0;
    static P2P = 1;

    get isConnected() {
        return this.seed && this.seed.signal.isConnected;
    }

    fetch(url, { forceCDN = this.forceCDN, timeout = this.timeout } = {}) {
        let type = DataBox.P2P;
        return this.getFileFromP2P(url, { timeout, forceCDN })
            .catch(err => {
                logInfo(err, 2);
                type = DataBox.CDN;
                return this.getFileFromCDN(url);
            })
            .then(({ buf, speed, starttime, loadtime, size }) => {
                if (isWebRTCSupported && this.seed) {
                    let partName = this.partName(this.id, url);
                    this.seed.addPart(this.id, url, partName, buf, type);
                }
                if (isFunction(this.onreport)) {
                    try {
                        this.onreport(buf.byteLength, type);
                    } catch (err) {
                        logError(err);
                    }
                }
                return {
                    buf,
                    type,
                    starttime,
                    loadtime,
                    speed,
                    size,
                    p2pbw: type === DataBox.P2P ? buf.byteLength : 0,
                    cdnbw: type === DataBox.CDN ? buf.byteLength : 0,
                    support: isWebRTCSupported ? 1 : 0
                };
            });
    }

    getFileFromCDN(url) {
        let options = {
            mode: 'cors'
        };
        let starttime = +Date.now();

        return fetch(url, options)
            .then(res => {
                if (res.status == 200 || res.status == 206) {
                    return res.arrayBuffer();
                } else {
                    throw new Error(res.statusText);
                }
            })
            .then(buf => {
                let endtime = +Date.now();
                let loadtime = endtime - starttime;

                logInfo('从CDN获取模块', buf.byteLength, 4);
                return {
                    buf,
                    starttime,
                    loadtime,
                    size: buf.byteLength,
                    speed: buf.byteLength / loadtime //kbps
                };
            });
    }

    getFileFromP2P(
        url,
        { forceCDN = this.forceCDN, timeout = this.timeout } = {}
    ) {
        if (forceCDN) return Promise.reject(new Error('强制使用CDN'));
        if (!isWebRTCSupported)
            return Promise.reject(new Error('WebRTC不支持'));

        if (!this.isConnected) {
            this.trytimes--;
            if (this.trytimes <= 0) {
                if (this.seed) {
                    this.seed.destory();
                    this.seed = null;
                }
            }
            return Promise.reject(new Error('还没连上P2P服务'));
        }

        if (!this.seed) return Promise.reject(new Error('请重新初始化'));

        let part = this.partName(this.id, url);
        let starttime = +Date.now();

        return this.seed.fetch(this.id, part, timeout).then(buf => {
            let endtime = +Date.now();
            let loadtime = endtime - starttime;

            logInfo('从P2P获取模块', buf.byteLength, 4);

            return {
                buf,
                starttime,
                loadtime,
                size: buf.byteLength,
                speed: buf.byteLength / loadtime //kbps
            };
        });
    }

    partName(videoId, url) {
        let name = filename(url);
        return `${videoId}-${name}`;
    }

    destory() {
        if (this.seed) {
            this.seed.destory();
            this.seed = null;
        }
    }
}
